#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netdb.h>
#include <netinet/ether.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>
#include <syslog.h>

extern "C" {
#include <pcap.h>
}
#include <odbc++/drivermanager.h>
#include <odbc++/connection.h>
#include <odbc++/resultset.h>
#include <odbc++/resultsetmetadata.h>
#include <odbc++/preparedstatement.h>

#include <cstdio>
#include <cmath>
#include <iostream>
#include <memory>
#include <sstream>
#include <list>
#include <map>

using namespace odbc;
using namespace std;

#include "ip.h"
#include "prio.h"
#include "dns.h"
#include "monlist.h"


static pthread_mutex_t SqlWriteMutex;

auto_ptr<Connection> con;

void	SQLConnect(char *connectionInfo)
{
	cout << "Connecting to " << connectionInfo
					<< "..." << flush;
	con = auto_ptr<Connection>(DriverManager::getConnection(connectionInfo));
	cout << " done." << endl;
	pthread_mutex_init(&SqlWriteMutex, NULL);
}

// odbc ISO YYYY-MM-DD HH24:MI:SS.FFF
//
static void timevalToDate(const struct timeval *in, char *out)
{
   struct tm *ti = localtime(&in->tv_sec);
	sprintf(out, "%04d-%02d-%02d %02d:%02d:%02d.%d", 
			ti->tm_year + 1900, ti->tm_mon + 1, ti->tm_mday,
			ti->tm_hour, ti->tm_min, ti->tm_sec,
			in->tv_usec);
}

bool	SQlSaveRecord(CRecord *cr, string interfaceName,  struct timeval *now)
{
	bool retval = true;

	pthread_mutex_lock(&SqlWriteMutex);
	std::auto_ptr<PreparedStatement> pstmt = std::auto_ptr<PreparedStatement>(con->prepareStatement
		(
			"INSERT INTO connections (src_ip_serial, dst_ip_serial, "
				"port_src, port_dst, "
				"protocol_number, "
				"first_seen, last_seen, packets, "
				"volume, interface, flushed_at, captured_data) "
					"VALUES (?, ?,"		// 1 2
							"?, ?,"			// 3 4
							"?,"				// 5 
							"?, ?, ?,"		// 6 7 8 
							"?, ?, ?, ?)"));	// 9 10 11 12

	//f_volume = cr->volume_m;
	//f_packets = cr->packets_m;
	pstmt->setInt(1, cr->srcSerial_m); pstmt->setInt(2, cr->dstSerial_m);
	pstmt->setInt(3, cr->port_src_m); pstmt->setInt(4, cr->port_dst_m);
	pstmt->setInt(5, cr->ip_proto_m);	// protocol
	char	firstUpdate[30];
	timevalToDate(&cr->firstUpdate_, firstUpdate);
	pstmt->setString(6, firstUpdate);

	char	lastUpdate[30];
	timevalToDate(&cr->lastUpdate_, lastUpdate);
	pstmt->setString(7, lastUpdate);	

	pstmt->setInt(8, cr->packets_m);	
	pstmt->setInt(9, cr->volume_m);
	pstmt->setString(10, interfaceName);

	char	flushed_at[30];
	if (now) {
		timevalToDate(now, flushed_at);
		pstmt->setString(11, flushed_at);	
	} else {
		pstmt->setNull(11, Types::VARCHAR);
	}
	// TODO: binary saved data
	pstmt->setNull(11, Types::LONGVARBINARY);
	pthread_mutex_unlock(&SqlWriteMutex);
	
	pstmt->executeUpdate();
	// check return value
	return retval;
}

int	SQLGetDNS(string resolvedName, const struct in_addr& ip, const char *mac)
{
	int		retCode= 0;
	char	ip_char[inet_to_stringSize];
	inet_to_char(ip, ip_char);

//	q_hostname = resolvedName.c_str();
//	q_ip_address = ip_char;

	std::auto_ptr<PreparedStatement> pstmt = std::auto_ptr<PreparedStatement>(con->prepareStatement
		("SELECT serial "
										"FROM dnsentry "
										"WHERE hostname = ? AND ip = ?"));
	pstmt->setString(1, resolvedName);
	pstmt->setString(2, ip_char);
	for (;;) {
		auto_ptr<ResultSet> rs = auto_ptr<ResultSet>(pstmt->executeQuery());

		if (!rs->next()) {	// no records, add to db
			std::auto_ptr<PreparedStatement> pinsert = std::auto_ptr<PreparedStatement>(con->prepareStatement
				("INSERT INTO dnsentry  (hostname, ip, mac) "
												"VALUES (?, ?, ?)"));
			pinsert->setString(1, resolvedName);
			pinsert->setString(2, ip_char);
			if (mac != NULL) {
				pinsert->setString(3, mac);
			} else {
				pinsert->setNull(3,Types::LONGVARCHAR);
			}
			try {
				pinsert->executeUpdate();
			} catch(SQLException& /*e*/) { 
				cerr << "INSERT INTO dnsentry failed" << endl;
				retCode = false;
				return -1;
			}
		} else {
			rs->getRow();
			retCode = rs->getInt(1);
			break;
		}
	}
//	pthread_mutex_unlock(&SqlWriteMutex);
	return retCode;
}

